#include "base_cpp/output.h"

#include "reaction/reaction_product_enumerator.h"
#include "reaction/reaction_enumerator_state.h"

#include "base_cpp/gray_codes.h"
#include "base_c/bitarray.h"
#include "base_cpp/tlscont.h"
#include "graph/graph.h"
#include "graph/embedding_enumerator.h"
#include "graph/spanning_tree.h"
#include "molecule/molecule.h"
#include "molecule/molecule_decomposer.h"
#include "molecule/canonical_smiles_saver.h"
#include "graph/dfs_walk.h"

ReactionProductEnumerator::ReactionProductEnumerator( const Reaction &reaction ) : TL_CP_GET(_product_aam_array),
                               TL_CP_GET(_smiles_array), TL_CP_GET(_tubes_monomers),
                               _reaction(reaction), is_all_emb(false), is_self_react(false), 
                               is_one_tube(false), max_deep_level(2), max_product_count(1000)
{
   _product_aam_array.clear();
   _smiles_array.clear();
   _tubes_monomers.clear();
   _product_count = 0;
   product_proc = 0;
}

void ReactionProductEnumerator::addMonomer( int reactant_idx, const Molecule &monomer )
{
   _reaction_monomers.addMonomer(reactant_idx, monomer);
}

void ReactionProductEnumerator::clearReactantMonomers( int reactant_idx )
{
   QS_DEF(Array<int>, unused_monomers);
   unused_monomers.clear();

   for (int i = _reaction_monomers._monomers.size() - 1; i >= 0; i--)
      if (_reaction_monomers._reactant_indexes[i] == reactant_idx)
         _reaction_monomers.removeMonomer(i);
}

const Molecule & ReactionProductEnumerator::getMonomer( int reactant_idx, int index )
{
   return _reaction_monomers.getMonomer(reactant_idx, index);
}

const Molecule & ReactionProductEnumerator::getMonomer( int mon_index )
{
   return _reaction_monomers.getMonomer(mon_index);
}

const Reaction & ReactionProductEnumerator::getReaction( void )
{
   return _reaction;
}

int ReactionProductEnumerator::getMonomersCount( int reactant_idx )
{
   int monomers_count = 0;

   for (int i = 0; i < _reaction_monomers._reactant_indexes.size(); i++)
      if (_reaction_monomers._reactant_indexes[i] == reactant_idx)
         monomers_count++;

   return monomers_count;
}

void ReactionProductEnumerator::buildProducts( void )
{
   QS_DEF(Molecule, all_products);
   all_products.clear();

   for (int i = 0; i < _reaction_monomers.size(); i++)
   {
      if (_reaction_monomers._deep_levels[i] > 0)
      {
         _reaction_monomers.removeMonomer(i);
         i--;
      }
   }

   for (int i = _reaction.productBegin(); i != _reaction.productEnd(); i = _reaction.productNext(i))
   {
      const Molecule &product = _reaction.getMolecule(i);
      QS_DEF(Array<int>, mapping);
      mapping.clear();

      all_products.mergeWithMolecule(product, &mapping);
      _product_aam_array.expand(all_products.vertexEnd());
      for (int j = product.vertexBegin(); j != product.vertexEnd(); j = product.vertexNext(j))
            _product_aam_array[mapping[j]] = _reaction.getAAM(i, j);
   }

   all_products.calcImplicitHydrogens(true);
   all_products.cis_trans.build(all_products, NULL);
   //all_products.unfoldHydrogens(NULL);

   _smiles_array.clear();
   _product_count = 0;

   ReactionEnumeratorState rpe_state(_reaction, all_products, 
                      _product_aam_array, _smiles_array, _reaction_monomers, _product_count, _tubes_monomers);

   rpe_state.product_proc = product_proc;
   rpe_state.userdata = userdata;
   rpe_state.is_all_emb = is_all_emb;
   rpe_state.is_self_react = is_self_react;
   rpe_state.max_deep_level = max_deep_level;
   rpe_state.max_product_count = max_product_count;
   rpe_state.is_one_tube = is_one_tube;

   rpe_state.buildProduct();
}